#include "Board.h"
#include "GlobalStructs.h"
#include <iostream>
#include "Color.h"
#include "SDL_rotozoom.h"
using namespace std;

/////////////////// Public Functions ///////////////////

/*--- Constructors ---*/
Board::Board(SDL_Surface* a_bufferCopy)
	:m_currentPlayer(0),m_isDrakonPlaced(false) {
	
	m_bufferCopy = SDL_ConvertSurface(a_bufferCopy, a_bufferCopy->format, SDL_SWSURFACE); //create copy of m_buffer for drawing tiles

	m_gTiles = new GameTile*[BOARD_ARRAY_SIZE];

	// TEST THIS: MAKE SURE GAME TILES ARE INITIALIZED (m_hasTile == false)
	// initialize game tile list
	for(int i = 0; i < BOARD_ARRAY_SIZE; ++i){
		m_gTiles[i] = new GameTile[BOARD_ARRAY_SIZE];
	}
	m_players = new Player[NUMBER_OF_PLAYERS+1];
	// initialize players
	for(int i = 0; i < NUMBER_OF_PLAYERS+1; ++i){
		m_players[i].setID(i);
		m_players[i].setPos(Position(BOARD_START_Y,BOARD_START_X));
	//		Player(i,Position(BOARD_START_X,BOARD_START_Y));
	}
	m_camFocus = Position(BOARD_START_X,BOARD_START_Y);
	m_gridFocus = Position(INITIAL_FOCUS_COLUMN,INITIAL_FOCUS_ROW);
	// Initializes id array to find correct picture to tile
	setIdArray();
	setGridStartPos();
}

Board::~Board() {
	// Delete pointers
	for(int i = 0; i < BOARD_ARRAY_SIZE; ++i) {
		delete m_gTiles[i];
	}
	delete [] m_gTiles;
	delete m_players;
	
	// Free surfaces
	SDL_FreeSurface(m_bufferCopy);
}


list<Tile>& Board::getDeck(void) {
	return m_deck;
}

list<Tile>& Board::getDeckReference(void) {
	return m_deck;
}

Tile Board::getGameTile(int r, int c) {
	return m_gTiles[r][c].getTile();
}
Tile Board::getGameTile(Position pos) {
	return m_gTiles[pos.getX()][pos.getY()].getTile();
}
//Position Board::getIdArray(void) {
//	//return m_idArray;
//}
Position Board::getIdArrayPos(int a_id) {
	Position tempPos;

	tempPos.setX(m_idArray[a_id].getX());
	tempPos.setY(m_idArray[a_id].getY());

	return tempPos;
}
void Board::setBoardTile(Tile a_tile, int r, int c, int owner) {
	Position pos(c,r);
	m_gTiles[r][c].placeTile(a_tile,pos, owner);
}
void Board::removeBoardTileAndPutOnDeck(Position pos) {
	addATileToDeck(m_gTiles[pos.getY()][pos.getX()].popTile());
}
void Board::setDeck(list<Tile>& tempTileList) {
	m_deck = tempTileList;
}

Position Board::getGridFocus(void) {
	return m_gridFocus;
}

void Board::setGridFocus(Position a_gridFocus) {
	m_gridFocus = a_gridFocus;
}

void Board::setGridFocus(int row, int column) {
	m_gridFocus.setPos(column,row);
}

Position Board::getGridStart(void) {
	return m_gridStart;
}

void Board::setGridStart(Position a_gridStart) {
	m_gridStart = a_gridStart;
}

void Board::setGridStart(int row, int column) {
	m_gridStart.setPos(column,row);
}

void Board::printScreen(SDL_Surface * scr){
	for(int i = 0; i < BOARD_ARRAY_SIZE; i++){
		for(int j = 0; j < BOARD_ARRAY_SIZE; ++j){
			//cout << (m_gTiles[i][j].hasTile())?"1":"*";
		}
		//cout << endl;
	}	
}

void Board::printDeck(SDL_Surface * scr) {
	list<Tile>::iterator iter;
	int s = 0;
	//cout << m_deck.size();
	for(iter = m_deck.begin() ; iter != m_deck.end(); ++iter) {
		printf("\n[%i] Name: [%s] Flag: [%u]", s,(*iter).getName().c_str(), (*iter).getFlag());
		printf("\nName: [%s] EXITS > UP [%i] DOWN [%i] LEFT [%i] RIGHT [%i]"
			, (*iter).getName().c_str(), (*iter).hasExit(UP), (*iter).hasExit(DOWN)
			, (*iter).hasExit(LEFT), (*iter).hasExit(RIGHT));
		++s;
	}
	printf("\n\n");
}
Tile Board::getTopTileFromDeck() {
	if(m_deck.size() != 0) {
		Tile temp = m_deck.front();
		m_deck.pop_front();
		return temp;
	} else {
		//cout << endl << "ERROR! No more tiles in deck." << endl;
		Position pos(100,100);
		Tile temp(-1,"ERROR",pos);
		return temp;
	}
}

void Board::shuffleDeck(list<Tile>& tileList) {
	int shuffleXtimes = 300;

	for(int j = 0; j < shuffleXtimes; ++j) {
		int indexA = rand()%tileList.size();

		list<Tile>::iterator itA = tileList.begin();
		for(int i = 0; i < indexA; ++i,++itA);

		Tile temp = tileList.front();
		
		tileList.insert(itA,temp);
		tileList.pop_front();
	}
}

void Board::placeStartTile(Tile a_tile, Position pos) {
	m_gTiles[pos.getY()][pos.getX()].placeTile(a_tile,pos,NUMBER_OF_PLAYERS);
	m_deck.pop_front();
}
void Board::placeTile(Tile a_tile, Position pos, int owner) {
	m_gTiles[pos.getY()][pos.getX()].placeTile(a_tile,pos,owner);
}
void Board::initGameStart() {
	// hand each player 4 tiles
	for(int i = 0; i < NUMBER_OF_PLAYERS; ++i){
		for(int j = 0; j < 4; ++j){
			givePlayerACard(i);
		}
	}
}

void Board::givePlayerACard(int playerID) {
	if(m_deck.size() > 0) {
		Tile temp = m_deck.front();
		m_deck.pop_front();
		m_players[playerID].addATileToHand(temp);
	}
}

void Board::printPlayerHands() {
	for(int i = 0; i < NUMBER_OF_PLAYERS; ++i) {
		// for each player, get the size of their hand and print that tile info
		//cout << m_players[i].toString();
	}
}
// WARNING MIND CONTROL MAY NOT BE HANDLED FOR THIS, WARNING .GETHANDLIST MIGHT BE PASS BY VALUE
void Board::drawPlayerHand(int playerID, SDL_Surface * a_buffer, SDL_Surface * a_tileBGs, SDL_Surface * a_tileIcons) {
	int i = 0;
	SDL_Surface * tempSurface;
	SDL_Surface * resizedImage;
	Player thisPlayer = m_players[playerID];
	
	for(list<Tile>::iterator iter= thisPlayer.getHandList().begin();iter != thisPlayer.getHandList().end();++iter) {
		if(i < PLAYER_TILE_LIST_DISPLAY_MAX) {
			//SDL_Surface * tempSurface;
			if(HAND_TILE_IMAGE_MODIFIER_X != 1 && HAND_TILE_IMAGE_MODIFIER_Y != 1) {
				tempSurface = iter->getTileSurface
					(m_bufferCopy,a_tileBGs,a_tileIcons,m_idArray);

				//SDL_Surface * resizedImage;
				resizedImage = zoomSurface
					(tempSurface,HAND_TILE_IMAGE_MODIFIER_X,HAND_TILE_IMAGE_MODIFIER_Y,0);

				blit(resizedImage,a_buffer,(int)PLAYER_TILE_LIST_START_X+(i*(TILE_BG_WIDTH*HAND_TILE_IMAGE_MODIFIER_X)),PLAYER_TILE_LIST_START_Y);

				SDL_FreeSurface(tempSurface);
				SDL_FreeSurface(resizedImage);
			} else {
				tempSurface = iter->getTileSurface
					(m_bufferCopy,a_tileBGs,a_tileIcons,m_idArray);

				blit(tempSurface,a_buffer,(int)PLAYER_TILE_LIST_START_X+(i*(TILE_BG_WIDTH*HAND_TILE_IMAGE_MODIFIER_X)),PLAYER_TILE_LIST_START_Y);

				SDL_FreeSurface(tempSurface);
			}

			++i;
		}
	}
}
void Board::drawBoardTiles(SDL_Surface * a_buffer, SDL_Surface * a_tileBGs, SDL_Surface * a_tileIcons, Position a_mPos) {
	int i = 0;
	SDL_Surface * tempSurface;
	SDL_Surface * resizedImage;

	Position quad1 = convertScreenCoordsToGridPos(a_mPos.getX(),a_mPos.getY());
	Position quad2 = convertScreenCoordsToGridPos(a_mPos.getX()+(TILE_IMAGE_MODIFIER_X*TILE_BG_WIDTH),a_mPos.getY());
	Position quad3 = convertScreenCoordsToGridPos(a_mPos.getX(),a_mPos.getY()+(TILE_IMAGE_MODIFIER_Y*TILE_BG_HEIGHT));
	Position quad4 = convertScreenCoordsToGridPos(a_mPos.getX()+(TILE_IMAGE_MODIFIER_X*TILE_BG_WIDTH),a_mPos.getY()
		+(TILE_IMAGE_MODIFIER_Y*TILE_BG_HEIGHT));

	int rowMin,rowMax,columnMin,columnMax;
	rowMin = m_gridFocus.getY()-5;
	rowMax = m_gridFocus.getY()+5;
	columnMin = m_gridFocus.getX()-6;
	columnMax = m_gridFocus.getX()+6;
	
	if(rowMin >= 0) {
	} else {
		rowMin = 0;
	}
	if(rowMax <= BOARD_ARRAY_SIZE-1) {
	} else {
		rowMax = BOARD_ARRAY_SIZE;
	}

	if(columnMin >= 0) {
	} else {
		columnMin = 0;
	}
	if(columnMax <= BOARD_ARRAY_SIZE-1) {
	} else {
		columnMax = BOARD_ARRAY_SIZE;
	}
	
	for(int r=rowMin; r<rowMax; ++r) {
		for(int c=columnMin; c<columnMax; ++c) {
			Position rcPos(c,r);
			if(m_gTiles[r][c].hasTile()) {
				//if((rcPos.getX() == quad1.getX() && rcPos.getY() == quad1.getY()) || (rcPos.getX() == quad2.getX() && rcPos.getY() == quad2.getY())
				//	|| (rcPos.getX() == quad3.getX() && rcPos.getY() == quad3.getY())
				//	|| (rcPos.getX() == quad4.getX() && rcPos.getY() == quad4.getY())) {
				//if((getBoardTileXPos(c) > -200 && getBoardTileXPos(c) < SCREEN_WIDTH+100)
				//	&& (getBoardTileYPos(r) > -200 && getBoardTileYPos(r) < SCREEN_HEIGHT+100)) {
				
				tempSurface = m_gTiles[r][c].getGameTileSurface(m_bufferCopy,a_tileBGs,a_tileIcons,m_idArray,m_players,m_isDrakonPlaced);
				
				if(TILE_IMAGE_MODIFIER_X != 1 && TILE_IMAGE_MODIFIER_Y != 1) {
					resizedImage = zoomSurface
						(tempSurface,TILE_IMAGE_MODIFIER_X,TILE_IMAGE_MODIFIER_Y,1);

					SDL_Rect piece = {0,0,TILE_BG_WIDTH*TILE_IMAGE_MODIFIER_X
					,TILE_BG_HEIGHT*TILE_IMAGE_MODIFIER_Y};

					SDL_Rect pos = {getBoardTileXPos(c),getBoardTileYPos(r),0,0};
					SDL_BlitSurface(resizedImage, &piece, a_buffer, &pos);

					SDL_FreeSurface(tempSurface);
					SDL_FreeSurface(resizedImage);
				} else {
					SDL_Rect piece = {0,0,TILE_BG_WIDTH*TILE_IMAGE_MODIFIER_X
					,TILE_BG_HEIGHT*TILE_IMAGE_MODIFIER_Y};

					SDL_Rect pos = {getBoardTileXPos(c),getBoardTileYPos(r),0,0};
					SDL_BlitSurface(tempSurface, &piece, a_buffer, &pos);

					SDL_FreeSurface(tempSurface);
				}
				++i;
			}
		}
	}
}
// moves m_camFocus depending on CameraMovement Variable
bool Board::panCam(CameraMovement cm){
	switch(cm){
		case CAM_UP:{
			m_camFocus.setPos(m_camFocus.getX(),m_camFocus.getY()-1);
			break;
		}
		case CAM_DOWN:{
			m_camFocus.setPos(m_camFocus.getX(),m_camFocus.getY()+1);
			break;
		}
		case CAM_LEFT:{
			m_camFocus.setPos(m_camFocus.getX()-1,m_camFocus.getY());
			break;
		}
		case CAM_RIGHT:{
			m_camFocus.setPos(m_camFocus.getX()+1,m_camFocus.getY());
			break;
		}
	}
	// can get value if movement is valid (for checking borders),
	// most probably won't reach border by design
	return true;
}

void Board::drawBoardBG(SDL_Surface * a_destination, SDL_Surface * a_source) {
	SDL_Rect pos = {0,0,0,0};

	SDL_BlitSurface(a_source, NULL, a_destination, &pos);
}

void Board::drawGameGrid(SDL_Surface* a_screen)
{

	int rowMin,rowMax,columnMin,columnMax;
	rowMin = m_gridFocus.getY()-10;
	rowMax = m_gridFocus.getY()+10;
	columnMin = m_gridFocus.getX()-10;
	columnMax = m_gridFocus.getX()+10;
		
	if(rowMin < 0) rowMin = 0;
	if(rowMax > BOARD_ARRAY_SIZE-1) rowMax = BOARD_ARRAY_SIZE;
	if(columnMin < 0) columnMin = 0;
	if(columnMax > BOARD_ARRAY_SIZE) columnMax = BOARD_ARRAY_SIZE;

	for(int r=rowMin; r<rowMax; ++r) {
		for(int c=columnMin; c<columnMax; ++c) {
			SDL_Rect gridLineColumn = {(GRID_THICK_X+m_gridStart.getX())+(c*(getTileTrueWidth()+GRID_THICK_X))
				,m_gridStart.getY(),GRID_THICK_X,BOARD_ARRAY_SIZE*getTileTrueHeight()};
			SDL_FillRect(a_screen, &gridLineColumn, GRID_COLUMN_COLOR);
		}
		SDL_Rect gridLineRow = {m_gridStart.getX(),(GRID_THICK_Y+m_gridStart.getY())+(r*(getTileTrueHeight()+GRID_THICK_Y))
			,BOARD_ARRAY_SIZE*getTileTrueHeight(),GRID_THICK_Y};
		SDL_FillRect(a_screen, &gridLineRow, GRID_ROW_COLOR);
	}
}

Position Board::convertScreenCoordsToGridPos(int mouseX, int mouseY) {
	int row=0,column=0;
	int x=0,y=0;
	Position posToReturn = Position();
	for(int r=0;r<BOARD_ARRAY_SIZE;++r) {
		for(int c=0;c<BOARD_ARRAY_SIZE;++c) {
			x = getBoardTileXPos(c);
			y = getBoardTileYPos(r);
			
			if(mouseX >= x && mouseX <= (x+getTileTrueWidth())) {
				if(mouseY >= y && mouseY <= (y+getTileTrueHeight())) {
					posToReturn= Position(c,r);
				}
			}
		}
	}
	return posToReturn;
}

/////////////////// Private Functions ///////////////////

void Board::setIdArray(void) {
	int tileWidth = TILE_ICON_WIDTH;
	int tileHeight = TILE_ICON_HEIGHT;
	//for(int i = 0; i < NUM_OF_IMG_ICONS; ++i){
	//	m_idArray[i].setPos(
	//} MAYBE DO BY MATH AND EDIT TILES.bmp : )
	m_idArray[0].setPos(3*tileWidth,4*tileHeight);
	m_idArray[1].setPos(0*tileWidth,0*tileHeight);
	m_idArray[2].setPos(1*tileWidth,0*tileHeight);
	m_idArray[3].setPos(2*tileWidth,0*tileHeight);
	m_idArray[4].setPos(3*tileWidth,0*tileHeight);
	m_idArray[5].setPos(0*tileWidth,1*tileHeight);
	m_idArray[6].setPos(1*tileWidth,1*tileHeight);
	m_idArray[7].setPos(2*tileWidth,1*tileHeight);
	m_idArray[8].setPos(3*tileWidth,1*tileHeight);
	m_idArray[9].setPos(0*tileWidth,2*tileHeight);
	m_idArray[10].setPos(1*tileWidth,2*tileHeight);
	m_idArray[11].setPos(2*tileWidth,2*tileHeight);
	m_idArray[12].setPos(3*tileWidth,2*tileHeight);
	m_idArray[13].setPos(0*tileWidth,3*tileHeight);
	m_idArray[14].setPos(1*tileWidth,3*tileHeight);
	m_idArray[15].setPos(2*tileWidth,3*tileHeight);
	m_idArray[16].setPos(3*tileWidth,3*tileHeight);
	m_idArray[17].setPos(0*tileWidth,4*tileHeight);
	m_idArray[18].setPos(1*tileWidth,4*tileHeight);
	m_idArray[19].setPos(2*tileWidth,4*tileHeight);
	m_idArray[20].setPos(3*tileWidth,4*tileHeight);
}

void Board::blit(SDL_Surface* a_source,SDL_Surface* a_destination,Position a_position) {
	SDL_Rect pos = {a_position.getX(),a_position.getY(),0,0};

	SDL_BlitSurface(a_source, NULL, a_destination, &pos);
}

void Board::blit(SDL_Surface* a_source,SDL_Surface* a_destination,int a_x,int a_y) {
	SDL_Rect piece = {0,0,(Uint16)TILE_BG_WIDTH*HAND_TILE_IMAGE_MODIFIER_X,(Uint16)TILE_BG_HEIGHT*HAND_TILE_IMAGE_MODIFIER_Y};
	SDL_Rect pos = {a_x,a_y,0,0};
	SDL_BlitSurface(a_source, &piece, a_destination, &pos);
}

list<Tile>& Board::getCurrPlyrTileList(void) {
	return m_players[m_currentPlayer].getHandList();
}

void Board::setGridStartPos(void) {
	int x = (((SCREEN_WIDTH/2)-(getTileTrueWidth()/2))-(getTileTrueWidth()*m_gridFocus.getX()))-(GRID_THICK_X*(BOARD_ARRAY_SIZE/2));
	int y = (((SCREEN_HEIGHT/2)-(getTileTrueHeight()/2))-(getTileTrueHeight()*m_gridFocus.getY()))-(GRID_THICK_Y*(BOARD_ARRAY_SIZE/2));
	m_gridStart.setPos(x,y);
}

int Board::getTileTrueWidth(void) {
	return int(TILE_BG_WIDTH*TILE_IMAGE_MODIFIER_X);
}

int Board::getTileTrueHeight(void) {
	return int(TILE_BG_HEIGHT*TILE_IMAGE_MODIFIER_Y);
}

int Board::getBoardTileXPos(int column) {
	return m_gridStart.getX()+((column*(getTileTrueWidth()+GRID_THICK_X))+(GRID_THICK_X*2));
}

int Board::getBoardTileYPos(int row) {
	return m_gridStart.getY()+((row*(getTileTrueHeight()+GRID_THICK_Y))+(GRID_THICK_Y)*2);
}
bool Board::isThereAPlayerOnGameTile(int row, int column){
	// for each player check if their pos == this row/column
	bool playerIsOnTile = false;
	for(int i = 0; i < NUMBER_OF_PLAYERS; ++i){
		if(m_players[i].getPosition().equals(Position(column,row))){
			playerIsOnTile = true;
		}
	}
	return playerIsOnTile;
}
// WARNING CURRENTLY isThereAPlayerOnGameTile does not check if DRAKON IS THERE!
bool Board::isThereAPlayerOnGameTile(Position pos){
	// for each player check if their pos == this row/column
	bool playerIsOnTile = false;
	for(int i = 0; i < NUMBER_OF_PLAYERS; ++i){
		if(m_players[i].getPosition().equals(pos)){
			
			playerIsOnTile = true;
		}
	}
	if(m_isDrakonPlaced && m_players[NUMBER_OF_PLAYERS].getPosition().equals(pos)){
		playerIsOnTile = true;
	}
	return playerIsOnTile;
}
void Board::changeTurn(){
	m_currentPlayer++;
	if(m_currentPlayer == NUMBER_OF_PLAYERS){
		m_currentPlayer = 0;
	}
}
void Board::summonDrakon(Position pos){
	m_isDrakonPlaced = true;
	m_players[NUMBER_OF_PLAYERS].setPos(pos);
	
}

IntInt Board::getPlayerWithMostGoldLost(){
	IntInt info;
	int unluckiestPlayer;
	int mostgoldlost=0;
	for(int i = 0; i < NUMBER_OF_PLAYERS; i++){
		int gold = m_players->getCoinSack().getTotalGoldLost();
		if(gold >= mostgoldlost){
			mostgoldlost = gold;
			unluckiestPlayer = i;
		}
	}
	info = make_pair(unluckiestPlayer,mostgoldlost);
	return info;
}
IntInt Board::getPlayerWithLeastGoldLost(){
	IntInt info;
	int luckiestPlayer;
	int leastGoldLost = INT_MAX;
	for(int i = 0; i < NUMBER_OF_PLAYERS; i++){
		int gold = m_players->getCoinSack().getTotalGoldLost();
		if(gold < leastGoldLost){
			leastGoldLost = gold;
			luckiestPlayer = i;
		}
	}
	info = make_pair(luckiestPlayer,leastGoldLost);
	return info;
}
IntInt Board::getPlayerWithMostGoldGained(){
	IntInt info;
	int luckiestPlayer;
	int mostgoldwon=0;
	for(int i = 0; i < NUMBER_OF_PLAYERS; i++){
		int gold = m_players->getCoinSack().getTotalGoldReceived();
		if(gold >= mostgoldwon){
			mostgoldwon = gold;
			luckiestPlayer = i;
		}
	}
	info = make_pair(luckiestPlayer,mostgoldwon);
	return info;
}
IntInt Board::getPlayerWithLeastGoldGained(){
	IntInt info;
	int unluckiestPlayer;
	int leastGoldwon = INT_MAX;
	for(int i = 0; i < NUMBER_OF_PLAYERS; i++){
		int gold = m_players->getCoinSack().getTotalGoldReceived();
		if(gold < leastGoldwon ){
			leastGoldwon  = gold;
			unluckiestPlayer = i;
		}
	}
	info = make_pair(unluckiestPlayer,leastGoldwon);
	return info;
}